#pragma unmanaged

#include <BWAPI.h>
#include <BWTA.h>

#pragma managed

#include "BWAPI\Game.h"
#include "BWAPI\Player.h"
#include "BWAPI\Position.h"
#include "BWAPI\Unit.h"

using namespace System;
using namespace System::Configuration;
using namespace System::IO;
using namespace System::Reflection;
using namespace System::Runtime::InteropServices;

#include "AIManaged.h"

namespace BroodWar
{
	void Log(String^ string)
	{
		String^ fileName = Path::Combine(
			Environment::GetFolderPath(Environment::SpecialFolder::Personal),
			"text-from-lib.txt");
		StreamWriter^ file = gcnew StreamWriter(fileName, true);
		file->WriteLine(string);
		file->Close();

		IntPtr ptr = Marshal::StringToHGlobalAnsi(string);
		BWAPI::Broodwar->sendText((char*)ptr.ToPointer());
		Marshal::FreeHGlobal(ptr);
	}

	void LogException(String^ module, Exception^ ex)
	{
		Log(String::Format("Unexpected error occured during {0}: {1}{2}{3}", module, ex->Message, Environment::NewLine, ex->StackTrace));
		while((ex = ex->InnerException) != nullptr)
		{
			Log(String::Format("Internal error is: {0}{1}{2}", ex->Message, Environment::NewLine, ex->StackTrace));
		}
	}

	static AIManaged::AIManaged()
	{
		BWAPI::BWAPI_init();

		try
		{
			String^ currentLocation = Assembly::GetExecutingAssembly()->Location;
			String^ configFile = String::Format("{0}.config", currentLocation);
			ExeConfigurationFileMap^ fileMap = gcnew ExeConfigurationFileMap();
			fileMap->ExeConfigFilename = configFile;
			System::Configuration::Configuration^ config = ConfigurationManager::OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel::None);

			String^ path = config->AppSettings->Settings["Path"]->Value;
			String^ assemblyName = config->AppSettings->Settings["Assembly"]->Value;
			String^ type = config->AppSettings->Settings["Module"]->Value;

			if(!Path::IsPathRooted(path))
			{
				path = Path::Combine(Path::GetDirectoryName(currentLocation), path);
			}

			Assembly^ assembly = Assembly::LoadFrom(Path::Combine(path, assemblyName));
			if(assembly == nullptr)
			{
				Log(String::Format("Could not load assembly {0} from path {1}", assemblyName, path));
				return;
			}
			Type^ module = assembly->GetType(type);
			if(module == nullptr)
			{
				Log(String::Format("Could not load type {0}", type));
				return;
			}
			ConstructorInfo^ constructor = module->GetConstructor(gcnew array<Type^> { });
			if(constructor == nullptr)
			{
				Log(String::Format("Could not find constructor for type {0}", type));
				return;
			}
			aiBase = dynamic_cast<AiBase^>(constructor->Invoke(gcnew array<Object^> { }));
			if(aiBase == nullptr)
			{
				Log(String::Format("Could not create instance of type {0}", type));
				return;
			}

			Log("Startup successful");
		}
		catch(Exception^ ex)
		{
			Log(String::Format("Unexpected error occured during initialization: {0}{1}{2}", ex->Message, Environment::NewLine, ex->StackTrace));
		}
	}

	void AIManaged::Start()
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnStart();
		}
		catch(Exception^ ex)
		{
			LogException("OnStart", ex);
		}
	}
		
	void AIManaged::End(bool isWinner)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnEnd(isWinner);
		}
		catch(Exception^ ex)
		{
			LogException("OnEnd", ex);
		}
	}
		
	void AIManaged::Frame()
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnFrame();
		}
		catch(Exception^ ex)
		{
			LogException("OnFrame", ex);
		}
	}
		
	void AIManaged::SendText(System::String^ text)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnSendText(text);
		}
		catch(Exception^ ex)
		{
			LogException("OnSendText", ex);
		}
	}
		
	void AIManaged::ReceiveText(BWAPI::Player* player, System::String^ text)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnReceiveText(gcnew Api::Player(player), text);
		}
		catch(Exception^ ex)
		{
			LogException("OnReceiveText", ex);
		}
	}
		
	void AIManaged::PlayerLeft(BWAPI::Player* player)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnPlayerLeft(gcnew Api::Player(player));
		}
		catch(Exception^ ex)
		{
			LogException("OnPlayerLeft", ex);
		}
	}
		
	void AIManaged::NukeDetect(BWAPI::Position target)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnNukeDetect(gcnew Api::Position(target));
		}
		catch(Exception^ ex)
		{
			LogException("OnNukeDetect", ex);
		}
	}
		
	void AIManaged::UnitDiscover(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitDiscover(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitDiscover", ex);
		}
	}
		
	void AIManaged::UnitEvade(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitEvade(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitEvade", ex);
		}
	}
		
	void AIManaged::UnitShow(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitShow(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitShow", ex);
		}
	}
		
	void AIManaged::UnitHide(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitHide(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitHide", ex);
		}
	}
		
	void AIManaged::UnitCreate(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitCreate(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitCreate", ex);
		}
	}
		
	void AIManaged::UnitDestroy(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitDestroy(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitDestroy", ex);
		}
	}
		
	void AIManaged::UnitMorph(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitMorph(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitMorph", ex);
		}
	}
		
	void AIManaged::UnitRenegade(BWAPI::Unit* unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitRenegade(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitRenegade", ex);
		}
	}
		
	void AIManaged::SaveGame(System::String^ gameName)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnSaveGame(gameName);
		}
		catch(Exception^ ex)
		{
			LogException("OnSaveGame", ex);
		}
	}
		
	void AIManaged::UnitComplete(BWAPI::Unit *unit)
	{
		try
		{
			if(aiBase != nullptr)
				aiBase->OnUnitComplete(gcnew Api::Unit(unit));
		}
		catch(Exception^ ex)
		{
			LogException("OnUnitComplete", ex);
		}
	}
}
